"""
阿里云 OSS HTTP API 工具类
使用标准HTTP请求实现OSS文件上传，避免SDK版本冲突
"""
import base64
import hashlib
import hmac
import logging
import time
from datetime import datetime
from typing import Optional
from urllib.parse import quote

import requests

from config import CONFIG

logger = logging.getLogger(__name__)


class OSSHttpClient:
    """阿里云 OSS HTTP API 客户端"""
    
    def __init__(self):
        self.oss_config = CONFIG["oss"]
        self.access_key_id = self.oss_config["access_key_id"]
        self.access_key_secret = self.oss_config["access_key_secret"]
        self.endpoint = self.oss_config["endpoint"]
        self.bucket_name = self.oss_config["bucket_name"]
        self.base_url = f"https://{self.bucket_name}.{self.endpoint}"
    
    def _generate_gmt_date(self) -> str:
        """生成GMT格式的时间字符串"""
        return datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
    
    def _sign_request(self, method: str, resource: str, headers: dict) -> str:
        """
        生成OSS请求签名
        
        Args:
            method: HTTP方法 (PUT, GET, POST, DELETE)
            resource: 资源路径
            headers: 请求头
            
        Returns:
            签名字符串
        """
        # 构造签名字符串
        string_to_sign_list = [
            method,
            headers.get('Content-MD5', ''),
            headers.get('Content-Type', ''),
            headers.get('Date', '')
        ]
        
        # 添加CanonicalizedOSSHeaders
        oss_headers = {}
        for key, value in headers.items():
            if key.lower().startswith('x-oss-'):
                oss_headers[key.lower()] = value
        
        if oss_headers:
            sorted_headers = sorted(oss_headers.items())
            for key, value in sorted_headers:
                string_to_sign_list.append(f"{key}:{value}")
        
        # 添加CanonicalizedResource
        string_to_sign_list.append(resource)
        
        string_to_sign = '\n'.join(string_to_sign_list)
        
        # 使用HMAC-SHA1生成签名
        signature = base64.b64encode(
            hmac.new(
                self.access_key_secret.encode('utf-8'),
                string_to_sign.encode('utf-8'),
                hashlib.sha1
            ).digest()
        ).decode('utf-8')
        
        return f"OSS {self.access_key_id}:{signature}"
    
    def _calculate_content_md5(self, content: bytes) -> str:
        """计算内容的MD5值"""
        return base64.b64encode(hashlib.md5(content).digest()).decode('utf-8')
    
    def upload_file(self, object_name: str, file_path: str, content_type: str = 'application/octet-stream') -> bool:
        """
        上传文件到OSS
        
        Args:
            object_name: OSS对象名称（文件路径）
            file_path: 本地文件路径
            content_type: 文件MIME类型
            
        Returns:
            上传是否成功
        """
        try:
            # 读取文件内容
            with open(file_path, 'rb') as f:
                file_content = f.read()
            
            return self.upload_content(object_name, file_content, content_type)
            
        except Exception as e:
            logger.error(f"上传文件失败: {e}")
            return False
    
    def upload_content(self, object_name: str, content: bytes, content_type: str = 'application/octet-stream') -> bool:
        """
        上传内容到OSS
        
        Args:
            object_name: OSS对象名称（文件路径）
            content: 文件内容
            content_type: 文件MIME类型
            
        Returns:
            上传是否成功
        """
        try:
            # 构造请求头
            gmt_date = self._generate_gmt_date()
            content_md5 = self._calculate_content_md5(content)
            
            headers = {
                'Date': gmt_date,
                'Content-Type': content_type,
                'Content-MD5': content_md5,
                'Content-Length': str(len(content))
            }
            
            # 构造资源路径
            resource = f"/{self.bucket_name}/{object_name}"
            
            # 生成签名
            authorization = self._sign_request('PUT', resource, headers)
            headers['Authorization'] = authorization
            
            # 构造请求URL
            url = f"{self.base_url}/{quote(object_name, safe='/')}"
            
            # 发送PUT请求
            response = requests.put(
                url,
                data=content,
                headers=headers,
                timeout=30
            )
            
            if response.status_code == 200:
                logger.info(f"文件上传成功: {object_name}")
                return True
            else:
                logger.error(f"文件上传失败，状态码: {response.status_code}, 响应: {response.text}")
                return False
                
        except Exception as e:
            logger.error(f"上传内容失败: {e}")
            return False
    
    def get_object_url(self, object_name: str) -> str:
        """
        获取OSS对象的访问URL
        
        Args:
            object_name: OSS对象名称
            
        Returns:
            对象访问URL
        """
        return f"https://{self.bucket_name}.{self.endpoint}/{object_name}"


def upload_file_to_oss(file_path: str, object_name: str, content_type: str = 'application/octet-stream') -> Optional[str]:
    """
    上传文件到阿里云OSS
    
    Args:
        file_path: 本地文件路径
        object_name: OSS对象名称（文件路径）
        content_type: 文件MIME类型
        
    Returns:
        上传成功返回OSS URL，失败返回None
    """
    client = OSSHttpClient()
    
    if client.upload_file(object_name, file_path, content_type):
        return client.get_object_url(object_name)
    else:
        return None


def upload_content_to_oss(content: bytes, object_name: str, content_type: str = 'application/octet-stream') -> Optional[str]:
    """
    上传内容到阿里云OSS
    
    Args:
        content: 文件内容
        object_name: OSS对象名称（文件路径）
        content_type: 文件MIME类型
        
    Returns:
        上传成功返回OSS URL，失败返回None
    """
    client = OSSHttpClient()
    
    if client.upload_content(object_name, content, content_type):
        return client.get_object_url(object_name)
    else:
        return None